package com.wasu.sdk_ott.http.live

import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import com.wasu.UILApplication
import com.wasu.constant.Config
import com.wasu.constant.DVB_LIVE_DOMAIN_4K
import com.wasu.constant.IP_LIVE_DOMAIN_4K
import com.wasu.constant.SYSTEM_CATEGORY_4K_IPDVB_LIVE
import com.wasu.constant.SYSTEM_CATEGORY_4K_IPDVB_QIUSUO
import com.wasu.constant.TAG
import com.wasu.rxcache.CacheTarget
import com.wasu.rxcache.RxCache
import com.wasu.rxcache.stategy.CacheStrategy
import com.wasu.rxcache.stategy.IStrategy
import com.wasu.sdk_ott.utils.WasuTvModelUtil
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap

/**
 * <p>------------------------------------------------------
 * <p>Copyright (C) 2020 wasu company, All rights reserved.
 * <p>------------------------------------------------------
 * <p> 接口请求的一些辅助类
 * <p>
 *
 * @author Created by zhangmz
 * @date on 2023/8/1
 */

////////////////////////////////////////////////////////////


/**
 * 如果是dvb模式，且直播类型，true，使用ipepg系统请求数据
 * 只有在dvb直播播控，才使用ipepg域名，如果求索，则单独匹配第一个直播频道
 */
fun isIpepgDomain(category: String? = null) = !WasuTvModelUtil.isbIpMode() && (TextUtils.isEmpty(category) || SYSTEM_CATEGORY_4K_IPDVB_LIVE == category)

/**
 * 获得ip直播管理系统域名
 */
fun getIpLiveDomain(): String{
    val domain = if (Config.debugDomainSwitch) WasuTvModelUtil.getDebugDoamin("loms") else IP_LIVE_DOMAIN_4K
    return if (TextUtils.isEmpty(domain)) IP_LIVE_DOMAIN_4K else domain
}

/**
 * 获得IPEPG域名
 */
fun getIpepgDomain(): String{
    val domain = if (Config.debugDomainSwitch) WasuTvModelUtil.getDebugDoamin("newipepg") else DVB_LIVE_DOMAIN_4K
    return if (TextUtils.isEmpty(domain)) DVB_LIVE_DOMAIN_4K else domain
}

/**
 * ip直播系统参数code,新终端高配盒子
 * 建德的区域是01_4008  杭州市01_4001  省网测试03_100000001  省网嘉兴03_4002
 */
fun getLiveRegion(): String{
    if (WasuTvModelUtil.isbIpMode()){
        return analysisUserProfile()
    }
    val regionId = configureRegionId.ifEmpty { analysisUserProfile() }
    val liveType = WasuTvModelUtil.getLiveType()
    return if (liveType == 2) "${regionId}_1" else regionId
}

/**
 * 解析 UserProfile 获得 regionId
 */
private fun analysisUserProfile(): String{
    if (Config.sUserProfile.isNullOrEmpty()){
        return ""
    }
    val p = Config.sUserProfile.split("#")
    if (p.isEmpty()){
        return ""
    }
    if (p.size < 2){
        return "${p[0]}_"
    }
    return "${p[0]}_${p[1]}"
}

/**
 * 获得4K网关广告接口配置的区域id
 */
private val configureRegionId: String by lazy {
    val sp = UILApplication.getInstance().application.applicationContext.getSharedPreferences("wasu_loader", Context.MODE_MULTI_PROCESS)
    sp?.getString("regionId", "")?: ""
}


////////////////////////////////////////////////////////////

/**
 * 三个根据接口版本号匹配不一致，发送到body的广播action
 */
const val WASU_BOU_EVENT_CHANGED = "wasu_live_bou_changed_"
const val WASU_EP_TODAY_CHANGED = "wasu_ep_today_changed_"
const val WASU_EP_CHI_CHANGED = "wasu_ep_chi_event_changed_"

////////////////////////////////////////////////////////////

/**
 * IP直播分组频道数据加密/解密密钥，必须16位
 */
const val IPLIVE_ENCRYPT_KEY_CHANNEL = "live1970@channel"

/**
 * IP直播节目单数据加密/解密密钥，必须16位
 */
const val IPLIVE_ENCRYPT_KEY_PROGRAM = "live1970@program"

/**
 * 默认分组频道缓存文件名称，如果有区域code名称，则代替该默认值
 */
const val IPLIVE_CACHE_CHANNEL_KYE = "menuChannel"

/**
 * 默认分组频道排序后缓存文件名称，如果有区域code名称，则代替该默认值
 */
const val IPLIVE_CACHE_CHANNEL_SORT_KYE = "menuChannelSort"

/**
 * 默认所有频道当天节目单缓存文件名称，如果有区域code名称，则代替该默认值
 */
const val IPLIVE_CACHE_PROGRAM_ALLDAY_KYE = "programAllDay"

/**
 * 看电视模式，只保存中央一套的频道信息，写死的频道id去匹配的
 */
const val IPLIVE_CACHE_WATCHTV_CHANNEL_KYE = "watchTvChannel"

/**
 * 保存分组频道下，所有频道信息到缓存
 */
const val IPLIVE_CACHE_ALL_CHANNEL_KYE = "liveAllChannel"

/**
 * 默认节目单缓存文件名称，如果有区域code名称，则代替该默认值
 */
const val IPLIVE_CACHE_PROGRAM_KYE = "program"

/**
 * DVB频道授权列表缓存key，如果有区域code名称，则代替该默认值
 */
const val IPLIVE_CACHE_AUTH_KYE = "livePurchased"

/**
 * 直播所有频道，根据IPTV或DVB保存对应系统的频道列表数据
 */
var authorizeChannelMap: ConcurrentMap<String, Any?>? = null

/**
 * 将频道id保存起来，用于查询预约节目单时，匹配所在频道是否存在
 */
@Synchronized
fun addAuthorizeChannel(authorize: String){
    Thread {
        runCatching {
            if (authorizeChannelMap == null) authorizeChannelMap = ConcurrentHashMap() else authorizeChannelMap?.clear()
            addChannelGroupToMap(authorize, authorizeChannelMap)
            Log.v(TAG, "addAuthorizeChannel authorizeChannelMap size:${authorizeChannelMap?.size}")

            saveCache(defaultKey = IPLIVE_CACHE_ALL_CHANNEL_KYE, content = JSONObject(authorizeChannelMap).toJSONString())
        }.onFailure { it.printStackTrace() }
    }.start()
}

/**
 * 保存分组频道到map中
 */
private fun addChannelGroupToMap(authorize: String, map: ConcurrentMap<String, Any?>? = null){
    val bouObject = JSONObject.parseObject(authorize)
    if (bouObject == null || !bouObject.containsKey("bList")){
        return
    }
    val bouArray = bouObject.getJSONArray("bList")
    for (i in bouArray.indices) {
        val chJsonObject = bouArray.getJSONObject(i)
        if (chJsonObject == null || !chJsonObject.containsKey("chList")){
            continue
        }
        val chArray = chJsonObject.getJSONArray("chList")
        if (chArray.isNullOrEmpty()) {
            continue
        }
        for (j in chArray.indices) {
            val chObject = chArray.getJSONObject(j)
            val chId = chObject.getString("chId")
            if (chId.isNullOrEmpty() || map?.contains(chId) == true) {
                continue
            }
            // 单独保存中央一套频道信息，用于开机，看电视模式，查询频道信息快速
            if ("00000000000000000000000000005210" == chId){
                // 保持缓存中和接口数据结构一致
                val bouChJsonObject = JSONObject()
                bouChJsonObject["result"] = "0"
                val chListJsonArray = JSONArray()
                chListJsonArray.add(chObject)
                bouChJsonObject["chList"] = chListJsonArray
                saveCache(Config.getRegion(), IPLIVE_CACHE_WATCHTV_CHANNEL_KYE, bouChJsonObject.toString())
            }
            map?.put(chId, chObject)
        }
    }
}

/**
 * 预约节目单所在的频道是否存在
 */
fun verifyAuthorizeChannel(
    channelId: String?,
    execute: (chInfo: JSONObject?) -> Unit = {  }
) {
    if (channelId.isNullOrEmpty()){
        Log.e(TAG, "verifyAuthorizeChannel, channelId can not be null")
        return
    }
    Log.v(TAG, "channelId:$channelId; authorizeChannelMap size:${authorizeChannelMap?.size}")
    if (authorizeChannelMap.isNullOrEmpty()){
        loadCache(defaultKey = IPLIVE_CACHE_ALL_CHANNEL_KYE,
            success = {
                authorizeChannelMap = ConcurrentHashMap(JSONObject.parseObject(it).toMap())
                if (authorizeChannelMap?.contains(channelId) == true){
                    execute(authorizeChannelMap?.get(channelId) as? JSONObject)
                }else {
                    execute(null)
                }
            },
            failure = { e ->
                Log.v(TAG, "verifyAuthorizeChannel, authorizeChannelMap cache is error:$e")
                execute(null)
            })
    }else {
        if (authorizeChannelMap?.contains(channelId) == true){
            execute(authorizeChannelMap?.get(channelId) as? JSONObject)
        }
    }
}

/**
 * DVB模式下，求索数据取自IP直播管理系统，但是第一个直播频道，需要替换成IPEPG的数据（主要是播放地址）
 * 求索 channelId: 30CP230120141113421900（记录）
 */
fun addQiuSuoLiveChannelForDvb(category: String?, result: String?): String?{
    if (WasuTvModelUtil.isbIpMode() || SYSTEM_CATEGORY_4K_IPDVB_QIUSUO != category || result.isNullOrEmpty()){
        return result
    }
    if (authorizeChannelMap.isNullOrEmpty()){
        loadCache(defaultKey = IPLIVE_CACHE_ALL_CHANNEL_KYE,
            success = {
                authorizeChannelMap = ConcurrentHashMap(JSONObject.parseObject(it).toMap())
            },
            failure = {
                Log.v(TAG, "addQiuSuoLiveChannelForDvb, authorizeChannelMap cache is null")
            })
    }
    if (authorizeChannelMap.isNullOrEmpty()){
        Log.e(TAG, "addQiuSuoLiveChannelForDvb, authorizeChannelMap is null")
        return result
    }
    val qiuSuoJSONObject = JSONObject.parseObject(result)
    if (!qiuSuoJSONObject.containsKey("bList") || TextUtils.isEmpty(qiuSuoJSONObject.getString("bList"))){
        return result
    }
    val qiuSuobListJSONArray = qiuSuoJSONObject.getJSONArray("bList")
    if (qiuSuobListJSONArray.size <= 0){
        return result
    }
    val qiuSuoChannelJSONArray = qiuSuobListJSONArray.getJSONObject(0).getJSONArray("chList")
    if (qiuSuoChannelJSONArray.size <= 0){
        return result
    }
    val qiuSuoLiveChannelJSONObject = qiuSuoChannelJSONArray.getJSONObject(0)
    val qiuSuoLiveChId = qiuSuoLiveChannelJSONObject.getString("chId")
    if (authorizeChannelMap?.contains(qiuSuoLiveChId) == true){
        val qiuSuoLiveChannelIpepg = authorizeChannelMap?.get(qiuSuoLiveChId) as? JSONObject
        if (!TextUtils.isEmpty(qiuSuoLiveChannelIpepg?.getString("chId"))){
            qiuSuoChannelJSONArray.removeAt(0)
            qiuSuoChannelJSONArray.add(0, qiuSuoLiveChannelIpepg)
        }
    }
    return qiuSuoJSONObject.toJSONString()
}

/**
 * 由供外部调用的loader类继承
 */
abstract class BaseAttribute {

    /**
     * 获得正式或者测试环境域名，子类必须继承
     */
    abstract fun createDomain(): String

    /**
     * 对比出UI层缓存策略，获得RXCache可用策略
     *
            0
            只取网络，不取缓存，不保存缓存，无策略时也是默认选项
            1
            取网络数据，有数据回调并保存缓存，否则回调空不缓存
            3
            优先网络，有数据回调并保存缓存，无数据取缓存，无缓存回调空
            2
            只取缓存，无网络请求，无缓存返回空
            4
            优先缓存，返回缓存数据，无缓存取网络，返回网络数据，网络数据请求后并保存缓存
            6
            先取缓存数据，无则不回调，再读取网络数据，有无都回调，会回调两次，cp请求不支持该策略
            7
            有缓存，读取并回调缓存，再请求网络，刷新缓存，不回调网络数据
            无缓存，请求网络，添加缓存，回调网络数据
     *
     */
    fun createStrategy(strategy: String? = ""): IStrategy {
        return when(strategy){
            // 只取网络，不保存缓存
            "0" -> CacheStrategy.none()
            // 请求网络数据，网络数据请求成功后写入缓存
            "1" -> CacheStrategy.onlyRemote()
            // 只取缓存，无网络请求
            "2" -> CacheStrategy.onlyCache()
            // 优先网络，当网络请求异常，无网络数据时，去加载缓存，否则不读取缓存
            "3" -> CacheStrategy.firstRemote()
            // 优先缓存，当无缓存或者缓存获取异常时，再请求网络数据
            "4" -> CacheStrategy.firstCache()
            // 优先使用缓存并设置缓存有效期，有效期内返回缓存，过期返回网络请求
            "5" -> CacheStrategy.firstCacheTimeout(cacheInvalidityTime())
            // 先返回缓存，再返回网络请求数据，会回调两次
            "6" -> CacheStrategy.cacheAndRemote()
            else -> CacheStrategy.none()
        }
    }

    /**
     * 获得缓存失效时间，目前是从当前时间到凌晨的毫秒数
     */
    private fun cacheInvalidityTime(): Long{
        val c = Calendar.getInstance(Locale.CHINA)
        c.set(Calendar.HOUR_OF_DAY, 24)
        c.set(Calendar.MINUTE, 0)
        c.set(Calendar.SECOND, 0)
        val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)
        val time = format.format(Date(c.timeInMillis))
        Log.v(TAG, "cacheInvalidityTime time:$time; invalidity:${c.timeInMillis - System.currentTimeMillis()}")
        return c.timeInMillis - System.currentTimeMillis()
    }
}

/**
 * 加载缓存
 */
fun loadCache(
    region: String? = Config.getRegion(),
    defaultKey: String,
    success: (result: String) -> Unit,
    failure: (e: Throwable) -> Unit
){
    if (TextUtils.isEmpty(defaultKey)){
        failure(Throwable("load cache defaultKey can not be null"))
        return
    }
    val key = if (region.isNullOrEmpty()) defaultKey else "${region}_$defaultKey"
    RxCache.getDefault().loadSync<String>(
        key,
        String::class.java
    )?.data?.let {
        Log.v(TAG, "load cache key:$key success")
        success(it)
    }?: failure(Throwable("load cache key:$key failure"))
}

/**
 * 保存缓存
 */
fun saveCache(
    region: String? = Config.getRegion(),
    defaultKey: String,
    content: String
){
    val key = if (region.isNullOrEmpty()) defaultKey else "${region}_$defaultKey"
    RxCache.getDefault().save(key, content, CacheTarget.MemoryAndDisk)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : Observer<Boolean> {
            override fun onSubscribe(d: Disposable) { }
            override fun onNext(t: Boolean) {
                Log.v(TAG, "save cache key:$key success")
            }
            override fun onError(e: Throwable) {
                Log.v(TAG, "save cache key:$key failure:$e")
            }
            override fun onComplete() { }
        })
}